package com.eedi.framework.oauth2.service;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.eedi.framework.common.enums.UserTypeEnum;
import com.eedi.framework.common.exception.util.ServiceExceptionUtil;
import com.eedi.framework.oauth2.dal.dataobject.OrgOAuth2AccessTokenDO;
import com.eedi.framework.oauth2.dal.dataobject.OrgOAuth2CodeDO;
import com.eedi.framework.oauth2.enums.OrgOauth2ErrorCodeConstants;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * OAuth2 授予 Service 实现类
 *
 * @author 永聚长青源码
 */
@Service
public class OrgOAuth2GrantServiceImpl implements OrgOAuth2GrantService {

    @Resource
    private OrgOAuth2TokenService orgOauth2TokenService;
    @Resource
    private OrgOAuth2CodeService orgOauth2CodeService;
//    @Resource
//    private AdminAuthService adminAuthService;

    @Override
    public OrgOAuth2AccessTokenDO grantImplicit(String userId, UserTypeEnum userType,
                                                String clientId, List<String> scopes) {
        return orgOauth2TokenService.createAccessToken(userId, userType, clientId, scopes);
    }

    @Override
    public String grantAuthorizationCodeForCode(String userId, UserTypeEnum userType,
                                                String clientId, List<String> scopes,
                                                String redirectUri, String state) {
        return orgOauth2CodeService.createAuthorizationCode(userId, userType, clientId, scopes,
                redirectUri, state).getCode();
    }

    @Override
    public OrgOAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code,
                                                                       String redirectUri, String state) {
        OrgOAuth2CodeDO codeDO = orgOauth2CodeService.consumeAuthorizationCode(code);
        // 防御性编程
        Assert.notNull(codeDO, "授权码不能为空");
        // 校验 clientId 是否匹配
        if (!StrUtil.equals(clientId, codeDO.getOrgOauth2ClientId())) {
            throw ServiceExceptionUtil.exception(OrgOauth2ErrorCodeConstants.ORG_OAUTH2_GRANT_CLIENT_ID_MISMATCH);
        }
        // 校验 redirectUri 是否匹配
        if (!StrUtil.equals(redirectUri, codeDO.getRedirectUri())) {
            throw ServiceExceptionUtil.exception(OrgOauth2ErrorCodeConstants.ORG_OAUTH2_GRANT_REDIRECT_URI_MISMATCH);
        }
        // 校验 state 是否匹配
        // 数据库 state 为 null 时，会设置为 "" 空串
        state = StrUtil.nullToDefault(state, "");
        if (!StrUtil.equals(state, codeDO.getState())) {
            throw ServiceExceptionUtil.exception(OrgOauth2ErrorCodeConstants.ORG_OAUTH2_GRANT_STATE_MISMATCH);
        }

        // 创建访问令牌
        return orgOauth2TokenService.createAccessToken(codeDO.getUserId(), codeDO.getUserType(),
                codeDO.getOrgOauth2ClientId(), codeDO.getScopes());
    }

//    @Override
//    public OAuth2AccessTokenDO grantPassword(String username, String password, String clientId, List<String> scopes) {
//        UserTypeEnum userType = WebFrameworkUtils.getLoginUserType();
//        // 使用账号 + 密码进行登录
//        AdminUserDO user = adminAuthService.authenticate(username, password,userType);
//        // 防御性编程
//        Assert.notNull(user, "用户不能为空！");
//
//        // 创建访问令牌
//        return oauth2TokenService.createAccessToken(user.getOrgtemUserId(), userType, clientId, scopes);
//    }

    @Override
    public OrgOAuth2AccessTokenDO grantRefreshToken(String refreshToken, String clientId) {
        return orgOauth2TokenService.refreshAccessToken(refreshToken, clientId);
    }

    @Override
    public OrgOAuth2AccessTokenDO grantClientCredentials(String clientId, List<String> scopes) {
        // TODO 芋艿：项目中使用 OAuth2 解决的是三方应用的授权，内部的 SSO 等问题，所以暂时不考虑 client_credentials 这个场景
        throw new UnsupportedOperationException("暂时不支持 client_credentials 授权模式");
    }

    @Override
    public boolean revokeToken(String clientId, String accessToken) {
        // 先查询，保证 clientId 时匹配的
        OrgOAuth2AccessTokenDO accessTokenDO = orgOauth2TokenService.getAccessToken(accessToken);
        if (accessTokenDO == null || ObjectUtil.notEqual(clientId, accessTokenDO.getOrgOauth2ClientId())) {
            return false;
        }
        // 再删除
        return orgOauth2TokenService.removeAccessToken(accessToken) != null;
    }

}
